using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;

namespace A4G.Core
{
	public class AlignmentPath : List<AlignmentScore>
	{
		private const char gapCharacter = '-';
		private object _lockObject = new object();

		public AlignmentPath(string sequence1, string sequence2)
		{
			_sequence1 = sequence1;
			_sequence2 = sequence2;
		}

		private readonly string _sequence1;
		public string Sequence1
		{
			get { return _sequence1; }
		}

		private readonly string _sequence2;
		public string Sequence2
		{
			get { return _sequence2; }
		}

		public void Splice(AlignmentScore topLeftScore, List<AlignmentScore> subPath)
		{
			lock (_lockObject)
			{
				int topLeftIndex = IndexOf(topLeftScore) + 1;
				Debug.WriteLine(topLeftIndex.ToString());
				try
				{
					// workaround for mono-bug
					foreach (AlignmentScore score in subPath)
					{
						Insert(topLeftIndex++, score);
					}
					// InsertRange(topLeftIndex, subPath);
				}
				catch (Exception e)
				{
					throw new ApplicationException(string.Format("topLeftIndex = {0}", topLeftIndex), e);
				}
			}
		}

		public void WriteToFile(string filename)
		{
			string[] results = GetAlignmentResults();

			StreamWriter writer = new StreamWriter(filename);
			try
			{
				foreach (string result in results)
				{
					writer.WriteLine(result);
				}
			}
			finally
			{
				writer.Close();
			}
		}

		public string[] GetAlignmentResults()
		{
			List<char> alignedSequence1 = new List<char>();
			List<char> alignedSequence2 = new List<char>();

			int i = 0;
			int j = 0;
			foreach (AlignmentScore score in this)
			{
				if (score.Pointer == TracebackPointers.None)
				{
					break;
				}

				switch (score.Pointer)
				{
					case TracebackPointers.None:
						break;

					case TracebackPointers.Match:
					case TracebackPointers.Mismatch:

						if (i >= Sequence1.Length)
						{
							throw new ApplicationException("First sequence index out of bounds.");
						}
						if (j >= Sequence2.Length)
						{
							throw new ApplicationException("Second sequence index out of bounds.");
						}

						alignedSequence1.Add(Sequence1[i++]);
						alignedSequence2.Add(Sequence2[j++]);
						break;

					case TracebackPointers.GapInFirstSequence:

						if (j >= Sequence2.Length)
						{
							throw new ApplicationException("Second sequence index out of bounds.");
						}

						alignedSequence1.Add(gapCharacter);
						alignedSequence2.Add(Sequence2[j++]);
						break;

					case TracebackPointers.GapInSecondSequence:

						if (i >= Sequence1.Length)
						{
							throw new ApplicationException("First sequence index out of bounds.");
						}

						alignedSequence1.Add(Sequence1[i++]);
						alignedSequence2.Add(gapCharacter);
						break;

					default:
						break;
				}
			}

			if (i != Sequence1.Length)
			{
				throw new ApplicationException("The last index of the first sequence is incorrect.");
			}
			if (j != Sequence2.Length)
			{
				throw new ApplicationException("The last index of the second sequence is incorrect.");
			}

			List<string> results = new List<string>(3);
			results.Add(this[Count - 1].Value.ToString());

			StringBuilder builder = new StringBuilder();
			builder = builder.Append(alignedSequence1.ToArray());
			results.Add(builder.ToString());

			builder.Remove(0, builder.Length);
			builder = builder.Append(alignedSequence2.ToArray());
			results.Add(builder.ToString());

			foreach (string result in results)
			{
				Trace.WriteLine(result);
			}

			return results.ToArray();
		}
	}
}
